package club.cearnach.crawler.action;

import club.cearnach.core.enumeration.MovieTypeEnum;
import club.cearnach.core.property.MovieCrawlerProperties;
import club.cearnach.crawler.util.GlobalThreadPool;
import club.cearnach.entity.*;
import com.alibaba.fastjson.JSONObject;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 电影信息爬虫
 *
 * @author 阮胜
 * @date 2018/5/4 17:00
 */
public class MovieCrawler {
    private final class MovieCrawlerMultiThreading implements Runnable {
        private final JSONObject jsonObject;
        private final Set<Movie> movieSet;
        private final CountDownLatch latch;

        MovieCrawlerMultiThreading(JSONObject jsonObject, Set<Movie> movieSet, CountDownLatch latch) {
            this.jsonObject = jsonObject;
            this.movieSet = movieSet;
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                movieSet.add(parseMovieInfo(jsonObject));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
        }
    }

    /**
     * 根据电影类型获取电影信息
     *
     * @param movieType 电影类型
     * @param start     开始位置
     * @param limit     获取数量
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    @SuppressWarnings("unchecked")
    public Set<Movie> obtainMovieInfoByMovieType(MovieTypeEnum movieType, int start, int limit) throws IOException, InterruptedException {
        String url = MovieCrawlerProperties.getUrlObtainMovieInfoByType(movieType.getName(), start, limit);
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url)
                .build();
        Call call = okHttpClient
                //.newBuilder().proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("1.119.193.36", 8080)))
                //.build()
                .newCall(request);
        Response response = call.execute();
        assert response.body() != null;
        String json = response.body().string();
        JSONObject jsonObject = JSONObject.parseObject(json);
        List<JSONObject> jsonList = (List<JSONObject>) jsonObject.get("subjects");
        int size = jsonList.size();
        Set<Movie> movieSet = Collections.synchronizedSet(new HashSet<>(size));
        CountDownLatch latch = new CountDownLatch(size);
        for (JSONObject o : jsonList) {
            GlobalThreadPool.POOL.execute(new MovieCrawlerMultiThreading(o, movieSet, latch));
        }
        if (!latch.await(5, TimeUnit.MINUTES)) {
            System.out.println("获取信息超时");
        }
        return movieSet;

    }

    /**
     * 解析电影信息
     *
     * @param o json对象
     * @return
     * @throws IOException
     */
    private Movie parseMovieInfo(JSONObject o) throws IOException {
        String movieUrl = o.get("url").toString();
        String rate = o.get("rate").toString();
        String title = o.get("title").toString();
        String id = o.get("id").toString();
        Document doc = Jsoup.connect(movieUrl).get();
        String director = doc.select("div #info .attrs").get(0).text();
        String actors = doc.select("div #info .attrs").get(2).text();
        String matchDivInfoPl = "div #info .pl";
        String type = doc.select(matchDivInfoPl).get(3).nextElementSibling().text();
        String location = doc.select(matchDivInfoPl).get(4).nextSibling().toString();
        String language = doc.select(matchDivInfoPl).get(5).nextSibling().toString();
        String initialReleaseDate = doc.select(matchDivInfoPl).get(6).nextElementSibling().text();
        String duration = doc.select(matchDivInfoPl).get(7).nextElementSibling().text();
        String description = doc.select("#link-report").text();
        String imgUrl = doc.select(".nbgnbg img").attr("src");
        String[] actorArr = actors.split("/");
        HashSet<Actor> actorSet = new HashSet<>(actorArr.length);
        for (String name : actorArr) {
            Person person = new Person(name);
            actorSet.add(new Actor(person));
        }
        String[] directorArr = director.split("/");
        HashSet<Director> directorSet = new HashSet<>();
        for (String name : directorArr) {
            Person person = new Person(name);
            directorSet.add(new Director(person));
        }
        return new Movie(title, Float.valueOf(rate), directorSet, actorSet, new MovieType(type), initialReleaseDate
                , new MovieLanguage(language), location, duration, description, imgUrl);
    }


    public static void main(String[] args) throws IOException, InterruptedException {
        resetName();
    }

    /**
     * 重置名称
     */
    private static void resetName() {
        String path = "d:/movie/image";
        File file = new File(path);
        File[] files = file.listFiles();
        for (int i = 0; i < files.length; i++) {
            File f = files[i];
            File nFile = new File(f.getPath().concat("_bak") + Math.random());
            f.renameTo(nFile);
        }
        File[] listFiles = file.listFiles();
        for (int i = 0; i < listFiles.length; i++) {
            File f2 = listFiles[i];
            f2.renameTo(new File("d:/movie/image/mv-it" + (i + 1) + ".jpg"));

        }
    }

    private static void crawlerAndSaveImageFile(MovieTypeEnum movieType, int start, int limit) throws IOException, InterruptedException {
        MovieCrawler movieCrawler = new MovieCrawler();
        Set<Movie> movieSet = movieCrawler.obtainMovieInfoByMovieType(movieType, start, limit);
        saveImageFile(movieSet, "l:/db/", "mv-it", ".jpg");
    }

    public static void saveImageFile(Set<Movie> movieSet, String path, String filePrefix, String fileSuffix) throws IOException {
        int i = 0;
        for (Movie movie : movieSet) {
            URL url = new URL(movie.getImageFileName());
            URLConnection urlConnection = url.openConnection();
            InputStream is = urlConnection.getInputStream();
            FileOutputStream fos = new FileOutputStream(path.concat(filePrefix).concat(String.valueOf(++i)).concat(fileSuffix));
            org.apache.tomcat.util.http.fileupload.IOUtils.copy(is, fos);
            is.close();
            fos.close();
        }
    }


}
